package com.xingshang.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xingshang.pojo.Work;
import com.xingshang.service.WorkService;
import com.xingshang.utils.JwtHelper;
import com.xingshang.utils.Result;
import com.xingshang.utils.ResultCodeEnum;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

@RestController
@RequestMapping("/api/work")
@Slf4j
public class WorkController {

    @Autowired
    private WorkService workService;

    @Value("${file.upload.dir}")
    private String uploadDir;

    @Autowired
    private JwtHelper jwtHelper;

    // 上传状态枚举
    private enum UploadStatus {
        PENDING,    // 等待中
        PROCESSING, // 处理中
        COMPLETED,  // 已完成
        FAILED      // 失败
    }

    // 上传信息类
    @Data
    private static class UploadInfo {
        private final Map<String, String> files = new ConcurrentHashMap<>();
        private final long createTime = System.currentTimeMillis();
        private volatile UploadStatus status = UploadStatus.PENDING;
        private String workName;
        private Long userId;
        private String content;
        private String errorMessage;
    }

    // 改进的缓存结构
    private static final Map<String, UploadInfo> UPLOAD_CACHE = new ConcurrentHashMap<>();


    /**
     * 获取用户作品列表
     *
     */
    @GetMapping("/list")
    public Result getUserWorks(@RequestHeader("token") String token) {
        try {
            Long userId = jwtHelper.getUserId(token);
            if (userId == null) {
                return Result.build(null, ResultCodeEnum.UNAUTHORIZED);
            }
            QueryWrapper<Work> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id", userId);

            return Result.ok(workService.list(queryWrapper));
        } catch (Exception e) {
            log.error("获取用户作品列表失败", e);
            return Result.build(null, ResultCodeEnum.SYSTEM_ERROR);
        }
    }

    @PostMapping("/create")
    public Result createWork(
            @RequestParam("lyrics") String lyrics,
            @RequestParam("file") MultipartFile file,
            @RequestParam("uploadId") String uploadId,
            @RequestParam("fileType") String fileType,
            @RequestParam("workName") String workName,
            @RequestHeader("token") String token
    ) {
        Long userId = jwtHelper.getUserId(token);

        try {
            // 基础验证
            if (file.isEmpty()) {
                return Result.build(null, ResultCodeEnum.FILE_EMPTY);
            }

            // 验证文件类型
            if (!isValidFileType(fileType)) {
                return Result.build(null, ResultCodeEnum.INVALID_FILE_TYPE);
            }

            // 获取或创建上传信息
            UploadInfo uploadInfo = UPLOAD_CACHE.computeIfAbsent(uploadId, k -> {
                UploadInfo info = new UploadInfo();
                info.setUserId(userId);
                info.setWorkName(workName);
                info.setContent(lyrics);
                return info;
            });

            // 检查是否是同一用户的上传
            if (!userId.equals(uploadInfo.getUserId())) {
                return Result.build(null, ResultCodeEnum.UNAUTHORIZED);
            }

            // 使用同步块确保原子性
            synchronized (uploadInfo) {
                // 检查上传状态
                if (uploadInfo.getStatus() == UploadStatus.PROCESSING) {
                    return Result.build(null, ResultCodeEnum.PROCESSING);
                }
                if (uploadInfo.getStatus() == UploadStatus.FAILED) {
                    return Result.build(null, ResultCodeEnum.UPLOAD_FAILED);
                }

                // 保存文件
                String filePath = saveFile(file, fileType);
                uploadInfo.getFiles().put(fileType, filePath);

                // 检查是否所有文件都已上传
                if (uploadInfo.getFiles().size() == 3) {
                    uploadInfo.setStatus(UploadStatus.PROCESSING);

                    // 异步处理合并操作
                    CompletableFuture.runAsync(() -> {
                        try {
                            processCompleteUpload(uploadInfo);
                        } catch (Exception e) {
                            log.error("处理上传失败", e);
                            uploadInfo.setStatus(UploadStatus.FAILED);
                            uploadInfo.setErrorMessage(e.getMessage());
                            cleanupFailedUpload(uploadInfo);
                        }
                    });

                    return Result.build(null, ResultCodeEnum.PROCESSING);
                }
            }

            return Result.build(null, ResultCodeEnum.SUCCESS);

        } catch (Exception e) {
            log.error("文件上传失败", e);
            return Result.build(null, ResultCodeEnum.SYSTEM_ERROR);
        }
    }

    // 获取上传状态的接口
    @GetMapping("/status/{uploadId}")
    public Result getUploadStatus(
            @PathVariable String uploadId,
            @RequestHeader("token") String token
    ) {
        Long userId = jwtHelper.getUserId(token);
        UploadInfo uploadInfo = UPLOAD_CACHE.get(uploadId);

        if (uploadInfo == null) {
            return Result.build(null, ResultCodeEnum.UPLOAD_NOT_FOUND);
        }

        if (!userId.equals(uploadInfo.getUserId())) {
            return Result.build(null, ResultCodeEnum.UNAUTHORIZED);
        }

        Map<String, Object> status = new HashMap<>();
        status.put("status", uploadInfo.getStatus());
        status.put("files", uploadInfo.getFiles());
        if (uploadInfo.getErrorMessage() != null) {
            status.put("error", uploadInfo.getErrorMessage());
        }

        return Result.build(status, ResultCodeEnum.SUCCESS);
    }

    private void processCompleteUpload(UploadInfo uploadInfo) throws IOException {
        try {
            // 合并音频文件
            String mergedAudioPath = mergeAudioFiles(
                    uploadInfo.getFiles().get("humming"),
                    uploadInfo.getFiles().get("instrument")
            );

            // 创建作品
            Work work = new Work();
            work.setUserId(uploadInfo.getUserId());
            work.setName(uploadInfo.getWorkName());
            work.setCoverUrl(uploadInfo.getFiles().get("cover"));
            work.setHummingUrl(uploadInfo.getFiles().get("humming"));
            work.setInstrumentUrl(uploadInfo.getFiles().get("instrument"));
            work.setMergedUrl(mergedAudioPath);
            work.setContent(uploadInfo.getContent());

            // 保存到数据库
            if (!workService.save(work)) {
                throw new IOException("保存作品信息失败");
            }

            uploadInfo.setStatus(UploadStatus.COMPLETED);
        } catch (Exception e) {
            uploadInfo.setStatus(UploadStatus.FAILED);
            uploadInfo.setErrorMessage(e.getMessage());
            throw e;
        }
    }

    private String saveFile(MultipartFile file, String fileType) throws IOException {
        String targetDir = uploadDir + File.separator + fileType;
        Path directory = Paths.get(targetDir);
        Files.createDirectories(directory);

        String fileName = generateUniqueFileName(file.getOriginalFilename());
        Path destinationPath = directory.resolve(fileName);

        // 使用原子操作保存文件
        Files.copy(file.getInputStream(), destinationPath, StandardCopyOption.REPLACE_EXISTING);

        return "/" + fileType + "/" + fileName;
    }

    private String generateUniqueFileName(String originalFilename) {
        String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        return UUID.randomUUID().toString() + "_" + System.nanoTime() + extension;
    }

    private boolean isValidFileType(String fileType) {
        return Arrays.asList("cover", "humming", "instrument").contains(fileType);
    }

    private void cleanupFailedUpload(UploadInfo uploadInfo) {
        // 清理已上传的文件
        uploadInfo.getFiles().values().forEach(filePath -> {
            try {
                Files.deleteIfExists(Paths.get(uploadDir + filePath));
            } catch (IOException e) {
                log.error("清理失败的上传文件时出错: {}", filePath, e);
            }
        });
    }

    /**
     * 定时清理过期的上传缓存和文件
     */
    @Scheduled(fixedRate = 1800000) // 每30分钟执行一次
    public void cleanUploadCache() {
        long currentTime = System.currentTimeMillis();
        long expirationTime = 1800000; // 30分钟过期

        UPLOAD_CACHE.entrySet().removeIf(entry -> {
            UploadInfo uploadInfo = entry.getValue();
            boolean isExpired = (currentTime - uploadInfo.getCreateTime()) > expirationTime;

            if (isExpired) {
                // 如果上传失败或过期，清理相关文件
                if (uploadInfo.getStatus() == UploadStatus.FAILED ||
                        uploadInfo.getStatus() == UploadStatus.PENDING) {
                    cleanupFailedUpload(uploadInfo);
                }
                return true;
            }
            return false;
        });
    }

    /**
     * 合并两个音频文件
     */
    private String mergeAudioFiles(String hummingPath, String instrumentPath) throws IOException {
        String fullHummingPath = uploadDir + hummingPath;
        String fullInstrumentPath = uploadDir + instrumentPath;

        // 检查文件是否存在
        if (!Files.exists(Paths.get(fullHummingPath))) {
            throw new IOException("哼唱文件不存在: " + fullHummingPath);
        }
        if (!Files.exists(Paths.get(fullInstrumentPath))) {
            throw new IOException("伴奏文件不存在: " + fullInstrumentPath);
        }

        // 生成合并后的文件路径
        String mergedFileName = UUID.randomUUID().toString() + ".mp3";
        String mergedDir = uploadDir + File.separator + "merged";
        Files.createDirectories(Paths.get(mergedDir));
        String mergedPath = mergedDir + File.separator + mergedFileName;

        // 构建 FFmpeg 命令
        List<String> command = Arrays.asList(
                "ffmpeg",
                "-y",
                "-i", fullHummingPath,
                "-i", fullInstrumentPath,
                "-filter_complex", "amix=inputs=2:duration=longest:dropout_transition=0",
                "-c:a", "libmp3lame",
                "-q:a", "4",
                mergedPath
        );

        ProcessBuilder processBuilder = new ProcessBuilder(command);
        processBuilder.redirectErrorStream(true);

        Process process = processBuilder.start();

        // 读取输出
        StringBuilder output = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(process.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
                log.debug("FFmpeg 输出: {}", line);
            }
        }

        // 等待进程完成
        try {
            int exitCode = process.waitFor();
            if (exitCode != 0) {
                throw new IOException("音频合并失败，退出码: " + exitCode + "\n" + output);
            }
        } catch (InterruptedException e) {
            process.destroyForcibly();
            Thread.currentThread().interrupt();
            throw new IOException("音频合并被中断", e);
        }

        // 检查生成的文件
        if (!Files.exists(Paths.get(mergedPath)) || Files.size(Paths.get(mergedPath)) == 0) {
            throw new IOException("合并后的文件不存在或为空: " + mergedPath);
        }

        return "/merged/" + mergedFileName;
    }
}